﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace FunctionGraph
{
    class BinaryCodeEvaluator : IEvaluator
    {
        Executable code;
        IntervalSet[] registers;
        int resultReg;
        SolutionEvaluationNode solNode;
        SolutionEvaluationVisitor solEvalVisitor;

        public BinaryCodeEvaluator(Executable code, SolutionEvaluationNode sol)
        {
            this.code = code;
            registers = new IntervalSet[code.RegisterCount];
            for (int i = 0; i < code.Constants.Count; i++)
            {
                registers[registers.Length-code.Constants.Count+i] = new IntervalSet(new Interval(code.Constants[i], code.Constants[i]));
            }
            resultReg = code.Instructions[code.Instructions.Count-1].Result;
            if (sol != null)
            {
                solNode = sol;
                var initor = new SolutionEvaluationTreeInitVisitor();
                solNode.Accept(initor);
                solEvalVisitor = new SolutionEvaluationVisitor();
                solEvalVisitor.evaluators = initor.evaluators.ToArray();
            }
            
        }

        public IntervalSet Evaluate(Interval x, Interval y)
        {
            registers[0] = new IntervalSet(x);
            registers[1] = new IntervalSet(y);
            for (int i = 0; i < code.Instructions.Count; i++)
            {
                var instr = code.Instructions[i];
                switch (instr.OpCode)
                {
                    case MathOperator.Add:
                        registers[instr.Result] = OperatorLibrary.Add(registers[instr.Operand1], registers[instr.Operand2]);
                        break;
                    case MathOperator.Sub:
                        registers[instr.Result] = OperatorLibrary.Sub(registers[instr.Operand1], registers[instr.Operand2]);
                        break;
                    case MathOperator.Mul:
                        registers[instr.Result] = OperatorLibrary.Mul(registers[instr.Operand1], registers[instr.Operand2]);
                        break;
                    case MathOperator.Div:
                        registers[instr.Result] = OperatorLibrary.Div(registers[instr.Operand1], registers[instr.Operand2]);
                        break;
                    case MathOperator.Mod:
                        //registers[instr.Result] = OperatorLibrary.Mod(registers[instr.Operand1], registers[instr.Operand2]);
                        break;
                    case MathOperator.Pow:
                        registers[instr.Result] = OperatorLibrary.Pow(registers[instr.Operand1], registers[instr.Operand2]);
                        break;
                    case MathOperator.Pow1:
                        registers[instr.Result] = OperatorLibrary.Pow1(registers[instr.Operand1], registers[instr.Operand2]);
                        break;
                    case MathOperator.Eql:
                        registers[instr.Result] = OperatorLibrary.Eql(registers[instr.Operand1], registers[instr.Operand2]);
                        break;
                    case MathOperator.Neq:
                        //registers[instr.Result] = OperatorLibrary.Neq(registers[instr.Operand1], registers[instr.Operand2]);
                        break;
                    case MathOperator.Leq:
                        //registers[instr.Result] = OperatorLibrary.Leq(registers[instr.Operand1], registers[instr.Operand2]);
                        break;
                    case MathOperator.Geq:
                        //registers[instr.Result] = OperatorLibrary.Geq(registers[instr.Operand1], registers[instr.Operand2]);
                        break;
                    case MathOperator.Less:
                        registers[instr.Result] = OperatorLibrary.Less(registers[instr.Operand1], registers[instr.Operand2]);
                        break;
                    case MathOperator.Greater:
                        registers[instr.Result] = OperatorLibrary.Greater(registers[instr.Operand1], registers[instr.Operand2]);
                        break;
                    case MathOperator.And:
                        break;
                    case MathOperator.Or:
                        break;
                    case MathOperator.Not:
                        break;
                    case MathOperator.Neg:
                        registers[instr.Result] = OperatorLibrary.Neg(registers[instr.Operand1]);
                        break;
                    case MathOperator.Sin:
                        registers[instr.Result] = OperatorLibrary.Sin(registers[instr.Operand1]);
                        break;
                    case MathOperator.Cos:
                        registers[instr.Result] = OperatorLibrary.Cos(registers[instr.Operand1]);
                        break;
                    case MathOperator.Abs:
                        registers[instr.Result] = OperatorLibrary.Abs(registers[instr.Operand1]);
                        break;
                    case MathOperator.Floor:
                        registers[instr.Result] = OperatorLibrary.Floor(registers[instr.Operand1]);
                        break;
                    case MathOperator.Ceil:
                        registers[instr.Result] = OperatorLibrary.Ceil(registers[instr.Operand1]);
                        break;
                    default:
                        break;
                }
            }
            return registers[resultReg];
        }


        public bool ContainsSolution(Interval x, Interval y)
        {
            if (solNode == null)
                return false;
            else
            {
                solEvalVisitor.x = x;
                solEvalVisitor.y = y;
                return solNode.Accept(solEvalVisitor);
            }
        }
    }

    internal class SolutionEvaluationTreeInitVisitor : ExpressionVisitor<object>
    {
        internal List<BinaryCodeEvaluator> evaluators = new List<BinaryCodeEvaluator>();
        public override object VisitConstant(ConstantNode constantNode)
        {
            throw new NotImplementedException();
        }

        public override object VisitVariable(VariableNode variableNode)
        {
            throw new NotImplementedException();
        }

        public override object VisitBinary(BinaryNode binaryNode)
        {
            throw new NotImplementedException();
        }

        public override object VisitUnary(UnaryNode unaryNode)
        {
            throw new NotImplementedException();
        }

        public override object VisitBinarySolutionEval(BinarySolutionEvaluationNode node)
        {
            node.Left.Accept(this);
            node.Right.Accept(this);
            return null;
        }

        public override object VisitExprSolutionEvaluationNode(ExprSolutionEvaluationNode node)
        {
            node.EvaluatorId = evaluators.Count;
            evaluators.Add(new BinaryCodeEvaluator(node.ExprExecutable, null));
            return null;
        }

    }
}
